package com.java8.lambda;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.junit.jupiter.api.Test;

/**
 * lambda表达式demo
 * @author zhangyang
 *
 */
public class LambdaTest {

	//@Test
	public void lambda() {
		//匿名内部类 做数据比较 代码比较多并且有用的代码少
		Comparator<Integer> comparator = new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				// TODO Auto-generated method stub
				return o1.compareTo(o2);
			}
		};
		System.out.println(comparator.compare(5, 4));
		//这样写就比较简捷 易懂
		//这就是高阶函数的用法-参数是一个函数 或者返回值是一个函数
		Comparator<Integer> com=(x,y)->x.compareTo(y);
		System.out.println(com.compare(5, 4));
		//需求 获取当前公司中年龄大于35的员工
		List<Employee> list = new ArrayList<>();
		list.add(new Employee("张三", 12));
		list.add(new Employee("李四", 39));
		list.add(new Employee("王五", 36));
		list.add(new Employee("赵六", 17));
		list.add(new Employee("张琪", 25));
		List<Employee> res=new ArrayList<>();
		//传统方式 for循环 外面定义新的list接收
		for(Employee em:list) {
			if(em.getAge()>35) {
				res.add(em);
			}
		}
		System.out.println(res.size());
		/**
		 * 如果筛选逻辑很多并且不一定按照年龄过滤 那么重复的for循环就会很多 优化方式 策略模式
		 * 将筛选编程策略接口-不同的方式实现就能做筛选过滤 但是这样代码还是很多
		 */
		//java8 lambda表达式
	List<Employee> list2 = list.stream().filter((x)->x.getAge()>35)
			.collect(Collectors.toList());
	System.out.println(list2.size());
	}
	
	/**
	 * lambda 表达式 参数->返回体部分
	 */
	/**
	 *无参数，无返回值
	 *()-> 返回提部分 如果多行还可以写{}括起来
	 *注意和匿名内部类一样，如果在内部中应用了与匿名内部类一个同级别的变量
	 *那么这个变量必须为final
	 */
	//@Test
	public void test1() {
		//多线程
		Runnable r1=new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("122433");
			}
		};
		r1.run();
		Runnable r2=()->System.out.println("hello world");
		r2.run();
		int num=0;
		Runnable r3=()->{
			//num在lambda中是final类型的 不能++
			//System.out.println(num++);
			System.out.println("hello world 多行");
			System.out.println("第二行代码");
		};
		
		r2.run();
		r3.run();
	}
	
	//@Test
	public void test2() {
		/**
		 * 一个参数 无返回值
		 */
		Consumer<String> com=x->System.out.println(x);
		com.accept("123");
		
		/**
		 * 一个参数 有返回值
		 * 有多条语句必须用{}包裹表示一个整体 一条语句省略大括号和return
		 */
		Comparator<Integer> campare=(x,y)->{
			System.out.println("1221212");
			return Integer.compare(x, y);
			};
		Comparator<Integer> campare1=(x,y)-> Integer.compare(x, y);
		System.out.println(campare1.compare(4, 5));
		System.out.println(campare.compare(4, 5));
		
		/**
		 * lambda表达式的参数类型可以省略不写 jvm编译器可以通过上下文推断出类型
		 * 这也是List<Employee> list = new ArrayList<>(); 为什么可以不写类型的原因
		 * 这在JDK1.7编译不通过
		 */
		/**
		 * 还是Comparator方法，注意排序里面加上规则reverse 就会编译不通过
		 * 这样会导致泛型推断有问题 所以当发现问题的时候 也是可以在参数部分加上类型泛型的
		 */
		List<String> list=Arrays.asList("张三","李世光","小泉纯一郎","伊布拉希莫维奇","武器打","呜呜六六");
		
		/**上面的集合我们需要按照字符串的长度进行排序，要求正序和倒序都要排 怎么做
		 * Comparator.comparingInt(item->item.length())这是正序不会报错
		 * 倒叙加上.reversed()之后参数类型是要在 reversed执行之后才能得出类型 
		 * 这里距离参数中间间隔了一层 所以获取不到参数类型编译不通过
		 * Comparator.comparingInt((String item)->item.length()).reversed()这样加上泛型就能通过了
		 */
		Collections.sort(list, Comparator.comparingInt(item->item.length()));
		System.out.println(list);
		Collections.sort(list, Comparator.comparingInt((String item)->item.length()).reversed());
		System.out.println(list);
	}
	
	//@Test
	public void test3() {
		/**
		 * 需求，集合排序 按照年龄排序，年龄相同按照姓名排序
		 */
		List<Employee> list = Arrays.asList(new Employee("张三", 12),
				new Employee("李四", 15),
				new Employee("王五", 12),
				new Employee("赵六", 33),
				new Employee("张琪", 33),
				new Employee("李白", 25)
				);
		list.sort((e1,e2)->{
			if(e1.getAge()!=e2.getAge()) 
				return Integer.compare(e1.getAge(), e2.getAge());
			else
				return e1.getName().compareTo(e2.getName());
		});
		for(Employee em:list) {
			System.out.println(em);
		}
	}
	
	//@Test
	public void test4() {
		Consumer<String> con=x->System.out.println(x);
		PrintStream ps=System.out;
		Consumer<String> con1=ps::println;
		con.accept("123");
		con1.accept("123");
		//注意方法体中的参数类型和返回值类型，必须要和函数式接口中参数列表和反回值类型一致
		//对象::方法名
		Consumer<String> con2=System.out::println;
		con2.accept("123");
		Employee employee = new Employee("张三",12);
		Supplier<String> s_name=employee::getName;
		System.out.println(s_name.get());
		//类::静态方法名
		Comparator<Integer> com=Integer::compare;
		System.out.println(com.compare(3, 4));
		//类::实例方法名 
		//注意，这里想要用类名::实例方法名 必须满足第一个参数式方法的调用对象,
		//并且第二个参数是需要引用方法的参数(或者引用方法无参数)才能使用
		BiPredicate<String,String> pre=String::equals;
		System.out.println(pre.test("123", "123"));
	}
	
	@Test
	public void test5() {
		/**
		 * 类构造器 类::new 
		 * 注意前提 对象构造方法的参数个数,必须和接口中的抽象方法参数列表一致
		 */
		Supplier<Employee> com=Employee::new;
		System.out.println(com.get().toString());
		//一个参数的时候 里面必须要有一个String类型的构造器
		Function<String,Employee> func=Employee::new;
		System.out.println(func.apply("张三").toString());
		BiFunction<String, Integer, Employee> bif=Employee::new;
		System.out.println(bif.apply("李四", 11).toString());
		//倒过来就不可以，原因就是因为没有第一个参数为Integer 第二个参数为String的构造器
		//BiFunction<Integer, String, Employee> bif=Employee::new;
	}
}

class Employee{
	private String name;
	private Integer age;
	
	
	public Employee(String name) {
		super();
		this.name = name;
	}
	
	public Employee(String name, Integer age) {
		super();
		this.name = name;
		this.age = age;
	}
	public Employee() {
		super();
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Employee [name=" + name + ", age=" + age + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((age == null) ? 0 : age.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Employee other = (Employee) obj;
		if (age == null) {
			if (other.age != null)
				return false;
		} else if (!age.equals(other.age))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}


}